package util.http.client4;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import util.file.FileOperation;

public class HttpClientUtil {
	
	private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
	

	public static final String POST = "POST";
	public static final String GET = "GET";
	public static final String PUT = "PUT";
	public static final String DELETE = "DELETE";
	public static final String UPLOAD_FILE = "UPLOADFILE";
	public static final String DOWNLOAD_FILE = "DOWNLOADFILE";
	
	public static final String UTF8 = "UTF-8";
	public static final String GBK = "GBK";
	public static final String GB2312 = "GB2312";
	
	public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
	public static final String CONTENT_TYPE_JSON = "application/json";
	
	private static final int SocketTimeout = 8 * 1000;
	private static final int ConnectTimeout = 8 * 1000;
	private static final int ConnectionRequestTimeout = 8 * 1000;
	
	private static final int MaxTotal = 500;
	private static final int DefaultMaxPerRoute = 50;
	private static final String  DEFAULT_UPLOAD_KEY = "file";
	
	
	private String url;
	private Map<String, String> paramForm;
	private String paramBody;
	private Map<String, String> headers;
	private String header;
	private String charset;
	private String uploadKey;
	private String pathName;
	
	
	private RequestConfig requestConfig;
	private PoolingHttpClientConnectionManager connectionManager;

	public HttpClientUtil(){
		this.charset = UTF8;
		this.uploadKey = DEFAULT_UPLOAD_KEY;
	}
	
	
	public RequestConfig getRequestConfig() {
		if (requestConfig == null) {
			RequestConfig Default = RequestConfig.DEFAULT;
			requestConfig = RequestConfig.copy(Default).setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout)
					.setConnectionRequestTimeout(ConnectionRequestTimeout).setRedirectsEnabled(true).setCircularRedirectsAllowed(false).build();
		}
		
		return requestConfig;
	}
	
	public PoolingHttpClientConnectionManager getConnectionManager() {
		if (connectionManager != null) {
			connectionManager = new PoolingHttpClientConnectionManager();
			connectionManager.setMaxTotal(MaxTotal);
			connectionManager.setDefaultMaxPerRoute(DefaultMaxPerRoute);
		}
		
		return connectionManager;
	}
	
	
	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public Map<String, String> getParamForm() {
		return paramForm;
	}

	public void setParamForm(Map<String, String> paramForm) {
		this.paramForm = paramForm;
	}

	public String getParamBody() {
		return paramBody;
	}

	public void setParamBody(String paramBody) {
		this.paramBody = paramBody;
	}

	public Map<String, String> getHeaders() {
		return headers;
	}

	public void setHeaders(Map<String, String> headers) {
		this.headers = headers;
	}

	public String getHeader() {
		return header;
	}

	public void setHeader(String header) {
		this.header = header;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public String getUploadKey() {
		return uploadKey;
	}

	public void setUploadKey(String uploadKey) {
		this.uploadKey = uploadKey;
	}

	public void setRequestConfig(RequestConfig requestConfig) {
		this.requestConfig = requestConfig;
	}

	public void setConnectionManager(PoolingHttpClientConnectionManager connectionManager) {
		this.connectionManager = connectionManager;
	}
	
	public String getPathName() {
		return pathName;
	}


	public void setPathName(String pathName) {
		this.pathName = pathName;
	}


	public HttpClientResponse post() throws ClientProtocolException, IOException{
		logger.info("post request url : {}", url);
		logger.info("post request paramForm : {}", paramForm);
		logger.info("post request paramBody : {}", paramBody);
		logger.debug("post request headers : {}", headers);
		logger.debug("post request charset : {}", charset);
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(this.url);
		
		if (requestConfig == null) {
			getRequestConfig();
		}
		
		httpPost.setConfig(requestConfig);
	
		boolean isForm = false;
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
				isForm = CONTENT_TYPE_FORM.equalsIgnoreCase(header.getValue());
			}
		}
		
		
		if (paramForm != null || isForm) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			if (paramForm != null) {
				for (Entry<String, String> form : paramForm.entrySet()) {
					nvps.add(new BasicNameValuePair(form.getKey(), form.getValue()));
				}
			}
			
			httpPost.setEntity(new UrlEncodedFormEntity(nvps,charset));
		}
		else {
			httpPost.setEntity(new StringEntity(paramBody, charset));
		}

		
		CloseableHttpResponse response = httpclient.execute(httpPost);

		HttpClientResponse responseBean = null;
		
		try {
			responseBean = doResp(POST,response);
		} finally {
		    response.close();
			httpPost.releaseConnection();
		}
		
		return responseBean;
	}
	
	public HttpClientResponse get() throws ClientProtocolException, IOException{
		logger.info("get request url : {}", url);
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		
		if (requestConfig == null) {
			getRequestConfig();
		}
		httpGet.setConfig(requestConfig);
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpGet.addHeader(header.getKey(), header.getValue());
			}
		}
	
		CloseableHttpResponse response = httpclient.execute(httpGet);
		
		HttpClientResponse responseBean = null;
		
		try {
			responseBean = doResp(GET,response);
		} finally {
		    response.close();
		    httpGet.releaseConnection();
		}

		
		return responseBean;
	}
	
	public HttpClientResponse put() throws ClientProtocolException, IOException{
		logger.info("put request url : {}", url);
		logger.info("put request paramForm : {}", paramForm);
		logger.info("put request paramBody : {}", paramBody);
		logger.debug("put request headers : {}", headers);
		logger.debug("put request charset : {}", charset);
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPut httpPut = new HttpPut(url);
		
		if (requestConfig == null) {
			getRequestConfig();
		}
		
		httpPut.setConfig(requestConfig);
		
		boolean isForm = false;
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPut.addHeader(header.getKey(), header.getValue());
				isForm = CONTENT_TYPE_FORM.equalsIgnoreCase(header.getValue());
			}
		}
		
		
		if (paramForm != null || isForm) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			if (paramForm != null) {
				for (Entry<String, String> form : paramForm.entrySet()) {
					nvps.add(new BasicNameValuePair(form.getKey(), form.getValue()));
				}
			}
			
			httpPut.setEntity(new UrlEncodedFormEntity(nvps,charset));
		}
		else {
			httpPut.setEntity(new StringEntity(paramBody, charset));
		}
		
		
		CloseableHttpResponse response = httpclient.execute(httpPut);
		HttpClientResponse responseBean = null;

		try {
		    responseBean = doResp(PUT,response);
		} finally {
		    response.close();
		    httpPut.releaseConnection();
		}
		
		return responseBean;
	}

	
	public  HttpClientResponse downloadFile() throws ClientProtocolException, IOException{
		logger.info("downloadFile request url : {}", url);
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		
		if (requestConfig == null) {
			getRequestConfig();
		}
		httpGet.setConfig(requestConfig);
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpGet.addHeader(header.getKey(), header.getValue());
			}
		}
	
		CloseableHttpResponse response = httpclient.execute(httpGet);
		HttpClientResponse responseBean = null;
		
		try {		    
		    responseBean = doResp(DOWNLOAD_FILE,response);
		} finally {
		    response.close();
		    httpGet.releaseConnection();
		}

		return responseBean;
	}
	
	public  HttpClientResponse uploadFile() throws ClientProtocolException, IOException{
		logger.info("uploadFile request url : {}", url);
		logger.info("put request paramForm : {}", paramForm);
		logger.info("uploadFile request pathName : {}", pathName);
		logger.debug("uploadFile request headers : {}", headers);

		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(url);
		
		if (requestConfig == null) {
			getRequestConfig();
		}
		
		httpPost.setConfig(requestConfig);
		
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
			}
		}

		FileBody fileBody = new FileBody(new File(pathName));
		MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().addPart(uploadKey, fileBody);
		
		for (Entry<String, String> val : paramForm.entrySet()) {
			StringBody eachVal = new StringBody(val.getValue(), ContentType.create("text/plain", charset));
			multipartEntityBuilder.addPart(val.getKey(), eachVal);
		}

		HttpEntity reqEntity = multipartEntityBuilder.build();
		httpPost.setEntity(reqEntity);
		
		CloseableHttpResponse response = httpclient.execute(httpPost);
		HttpClientResponse responseBean = null;
		
		try {
		    responseBean = doResp(UPLOAD_FILE,response);
		} finally {
		    response.close();
			httpPost.releaseConnection();
		}
		
		return responseBean;
	}
	
	
	private HttpClientResponse doResp(String type, CloseableHttpResponse response) throws IOException {

		logger.info("Request type " + type + " doResp response StatusLine : {}", response.getStatusLine());

		HttpEntity entity = response.getEntity();

		if (type.equals(DOWNLOAD_FILE)) {

			InputStream inputStream = entity.getContent();
			saveFile(inputStream, pathName);
			EntityUtils.consume(entity);

			return null;
		} else {
			String respStr = EntityUtils.toString(entity, charset);
			logger.info("Request type " + type + " doResp response entity : {}", respStr);

			EntityUtils.consume(entity);

			return buildResp(response, respStr);
		}
	}
	

	private  void saveFile(InputStream inputStream,String pathName){
		FileOperation fileOperation = new FileOperation(pathName);
		
		try {
			fileOperation.createBinaryFile(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
			logger.error("pathName", pathName);
		}
	}
	
	
	private  HttpClientResponse buildResp(CloseableHttpResponse response,String respStr){
		HttpClientResponse responseBean = new HttpClientResponse();
		responseBean.setContent(respStr);
		responseBean.setHttpStatusCode(response.getStatusLine().getStatusCode());
		
		HashMap<String, String> respHeader = new HashMap<String, String>();
		int index = 1;
		
		
		for (Header header : response.getAllHeaders()) {
			String name = header.getName();
			String value = header.getValue();

			logger.info("header name : {} --- value : {}", name, value);

			if (name == null || "".equals(name)) {
				if (value != null && !"".equals(value)) {
					respHeader.put("header" + index, header.getValue());
				}
			} else {
				respHeader.put(header.getName(), header.getValue());
				if (name.equals("Set-Cookie")) {
					responseBean.setCookies(value);
				}
			}

		}
		
		responseBean.setHeaders(respHeader);
		
		return responseBean;
	}



}

